#!/usr/bin/env python3
"""
机场态势对象数据导入脚本
从 Excel 文件读取机场数据并导入到 PostgreSQL 数据库
"""

import sys
import os
import logging
import asyncio
import re
from pathlib import Path
from datetime import datetime
from typing import Dict, Any, List

# 尝试导入 pandas 和 openpyxl，如果没有则提供备选方案
try:
    import pandas as pd
    HAS_PANDAS = True
except ImportError:
    HAS_PANDAS = False
    logging.warning("pandas 未安装，将尝试使用其他方式读取Excel文件")

try:
    import openpyxl
    HAS_OPENPYXL = True
except ImportError:
    HAS_OPENPYXL = False
    logging.warning("openpyxl 未安装，将尝试使用其他方式读取Excel文件")

# 添加项目根目录到 Python 路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

# 直接导入所需模块，避免触发 src.__init__ 中的图数据库初始化
import src.situation.database
import src.situation.models
import src.situation.service
import src.situation.schemas

from src.situation.database import get_db_session, engine, Base
from src.situation.models import SituationObject
from src.situation.service import SituationService
from src.situation.schemas import SituationObjectCreate, DeploymentStatus, ObjectType


# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('logs/airport_import.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


def read_excel_file(file_path: str) -> List[Dict[str, Any]]:
    """
    读取 Excel 文件并返回数据列表

    Args:
        file_path: Excel 文件路径

    Returns:
        包含机场数据的字典列表
    """
    try:
        if HAS_PANDAS and HAS_OPENPYXL:
            # 使用 pandas 读取 Excel 文件
            df = pd.read_excel(file_path)
            logger.info(f"使用 pandas 成功读取 Excel 文件，共 {len(df)} 行数据")

            # 转换为字典列表
            data = df.to_dict('records')

            # 记录列名
            logger.info(f"Excel 文件包含的列: {list(df.columns)}")

            return data
        else:
            # 使用 openpyxl 直接读取
            if not HAS_OPENPYXL:
                raise ImportError("需要安装 pandas 和 openpyxl 来读取 Excel 文件")

            from openpyxl import load_workbook

            wb = load_workbook(filename=file_path, read_only=True)
            sheet = wb.active

            # 读取标题行
            headers = [cell.value for cell in next(sheet.iter_rows(min_row=1, max_row=1))]
            logger.info(f"Excel 文件包含的列: {headers}")

            # 读取数据行
            data = []
            for row in sheet.iter_rows(min_row=2, values_only=True):
                if any(row):  # 跳过空行
                    row_dict = {headers[i]: row[i] for i in range(len(headers)) if i < len(row)}
                    data.append(row_dict)

            wb.close()
            logger.info(f"使用 openpyxl 成功读取 Excel 文件，共 {len(data)} 行数据")
            return data

    except Exception as e:
        logger.error(f"读取 Excel 文件失败: {e}")
        if not HAS_PANDAS or not HAS_OPENPYXL:
            logger.error("请安装必要的依赖: uv add pandas openpyxl")
        raise


def parse_coordinates(coord_str: str) -> Dict[str, float]:
    """
    解析坐标字符串
    支持格式：
    - 18.2404N, 42.6566E
    - 21°40′46″N 39°09′24″E

    Args:
        coord_str: 坐标字符串

    Returns:
        包含latitude和longitude的字典
    """
    if not coord_str:
        return {}

    try:
        # 清理字符串
        coord_str = str(coord_str).strip()

        # 尝试匹配度分秒格式
        dms_pattern = r'(\d+)°(\d+)′(\d+)″([NS])\s+(\d+)°(\d+)′(\d+)″([EW])'
        dms_match = re.search(dms_pattern, coord_str)
        if dms_match:
            lat_deg, lat_min, lat_sec, lat_dir = float(dms_match.group(1)), float(dms_match.group(2)), float(dms_match.group(3)), dms_match.group(4)
            lon_deg, lon_min, lon_sec, lon_dir = float(dms_match.group(5)), float(dms_match.group(6)), float(dms_match.group(7)), dms_match.group(8)

            # 转换为十进制度
            latitude = lat_deg + lat_min / 60 + lat_sec / 3600
            longitude = lon_deg + lon_min / 60 + lon_sec / 3600

            if lat_dir == 'S':
                latitude = -latitude
            if lon_dir == 'W':
                longitude = -longitude

            return {'latitude': latitude, 'longitude': longitude}

        # 尝试匹配十进制度格式
        decimal_pattern = r'([\d.]+)([NS])[\s,]+([\d.]+)([EW])'
        decimal_match = re.search(decimal_pattern, coord_str)
        if decimal_match:
            latitude = float(decimal_match.group(1))
            longitude = float(decimal_match.group(3))

            if decimal_match.group(2) == 'S':
                latitude = -latitude
            if decimal_match.group(4) == 'W':
                longitude = -longitude

            return {'latitude': latitude, 'longitude': longitude}

        logger.warning(f"无法解析坐标: {coord_str}")
        return {}

    except Exception as e:
        logger.warning(f"解析坐标失败: {coord_str}, 错误: {e}")
        return {}


def parse_altitude(altitude_str: str) -> float:
    """
    解析海拔字符串
    支持格式：
    - 6,858 ft（2090 m）
    - 20 ft（6 m）
    - 2,049–2,052 ft

    Args:
        altitude_str: 海拔字符串

    Returns:
        海拔（米）
    """
    if not altitude_str:
        return None

    try:
        altitude_str = str(altitude_str).strip()

        # 尝试从括号中提取米值
        meter_match = re.search(r'（(\d+(?:,\d+)*)\s*m', altitude_str)
        if meter_match:
            meters = meter_match.group(1).replace(',', '')
            return float(meters)

        # 如果没有米值，尝试从英尺转换
        feet_match = re.search(r'([\d,]+)(?:–[\d,]+)?\s*ft', altitude_str)
        if feet_match:
            feet = feet_match.group(1).replace(',', '')
            return float(feet) * 0.3048  # 英尺转米

        # 尝试直接提取数字
        numbers = re.findall(r'\d+(?:,\d+)*', altitude_str)
        if numbers:
            return float(numbers[0].replace(',', ''))

        logger.warning(f"无法解析海拔: {altitude_str}")
        return None

    except Exception as e:
        logger.warning(f"解析海拔失败: {altitude_str}, 错误: {e}")
        return None


def parse_runway(runway_str: str) -> Dict[str, Any]:
    """
    解析跑道信息
    支持格式：
    - 长 3350 m,宽45m
    - 长 3050 m,
    - 长度4205米,宽度60 米

    Args:
        runway_str: 跑道字符串

    Returns:
        包含length和width的字典
    """
    if not runway_str:
        return {}

    try:
        runway_str = str(runway_str).strip()
        result = {}

        # 提取长度
        length_match = re.search(r'长度?[：:]?\s*(\d+(?:,\d+)*)\s*[米m]', runway_str)
        if length_match:
            result['length'] = float(length_match.group(1).replace(',', ''))

        # 提取宽度
        width_match = re.search(r'宽度?[：:]?\s*(\d+(?:,\d+)*)\s*[米m]', runway_str)
        if width_match:
            result['width'] = float(width_match.group(1).replace(',', ''))

        return result

    except Exception as e:
        logger.warning(f"解析跑道信息失败: {runway_str}, 错误: {e}")
        return {}


def parse_area(area_str: str) -> float:
    """
    解析占地面积
    支持格式：
    - 约3.6 平方公里
    - 约2.6 平方公里

    Args:
        area_str: 面积字符串

    Returns:
        面积（平方公里）
    """
    if not area_str:
        return None

    try:
        area_str = str(area_str).strip()

        # 提取数字
        number_match = re.search(r'([\d.]+)', area_str)
        if number_match:
            return float(number_match.group(1))

        logger.warning(f"无法解析面积: {area_str}")
        return None

    except Exception as e:
        logger.warning(f"解析面积失败: {area_str}, 错误: {e}")
        return None


def convert_to_situation_object(airport_data: Dict[str, Any]) -> Dict[str, Any]:
    """
    将机场数据转换为态势对象格式

    Args:
        airport_data: 机场原始数据

    Returns:
        符合态势对象模型的数据字典
    """
    try:
        # 基础信息映射
        name = airport_data.get('名称') or airport_data.get('name')

        # 从名称中提取英文部分作为编码
        code = None
        if name:
            # 尝试提取括号中的内容作为编码
            code_match = re.search(r'\(([^)]+)\)', name)
            if code_match:
                code = code_match.group(1).strip()
            else:
                # 使用时间戳生成唯一编码
                import random
                code = f"AIRPORT-{datetime.now().strftime('%Y%m%d%H%M%S%f')}-{random.randint(1000, 9999)}"

        # 如果没有名称，生成默认名称
        if not name:
            name = f"未知机场-{datetime.now().strftime('%Y%m%d%H%M%S')}"
            code = f"AIRPORT-{datetime.now().strftime('%Y%m%d%H%M%S')}"

        # 解析坐标
        coordinates = parse_coordinates(airport_data.get('坐标') or airport_data.get('coordinates'))

        # 解析海拔
        altitude = parse_altitude(airport_data.get('海拔') or airport_data.get('altitude'))

        # 解析跑道信息
        runway_info = parse_runway(airport_data.get('跑道') or airport_data.get('runway'))

        # 解析占地面积
        area = parse_area(airport_data.get('占地面积') or airport_data.get('area'))

        # 表面类型
        surface = airport_data.get('表面') or airport_data.get('surface') or ''

        # 使用性质
        usage_type = airport_data.get('使用性质') or airport_data.get('usage_type') or ''

        # 使用时间
        usage_time = airport_data.get('使用时间') or airport_data.get('usage_time') or ''

        # 构建态势对象数据
        situation_data = {
            'object_type': ObjectType.INFRASTRUCTURE,  # 使用枚举类型
            'name': name,
            'code': code,
            'model': None,
            'organization': None,
            'deployment_status': DeploymentStatus.DEPLOYED,  # 使用枚举而不是字符串

            # 机场特定数据存储在 type_specific_data 中
            'type_specific_data': {
                'infrastructure_category': '机场',

                # 机场专属字段
                'altitude': altitude,
                'runway_length': runway_info.get('length'),
                'runway_width': runway_info.get('width'),
                'runway_surface': surface,
                'usage_type': usage_type,
                'usage_time': usage_time,
                'area': area,

                # 原始数据保留
                'coordinates_raw': airport_data.get('坐标'),
                'altitude_raw': airport_data.get('海拔'),
                'runway_raw': airport_data.get('跑道'),
            },

            # 位置信息
            'location': {
                **coordinates,
                'description': airport_data.get('坐标') or '',
            } if coordinates else {},

            # 适用场景
            'applicable_scenarios': ['民用航空', '军用航空', '物流运输', '应急救援'],

            # 标签
            'tags': ['机场', '基础设施', '航空'],

            # 其他元数据
            'extra_metadata': {
                'data_source': 'excel_import',
                'import_time': datetime.now().isoformat(),
            },

            # 审计信息
            'created_by': 'airport_import_script',
            'updated_by': 'airport_import_script'
        }

        logger.debug(f"成功转换机场数据: {name} (编码: {code})")
        return situation_data

    except Exception as e:
        logger.error(f"转换机场数据失败: {e}")
        logger.error(f"原始数据: {airport_data}")
        raise


async def import_airport_data(excel_file_path: str, batch_size: int = 10):
    """
    导入机场数据到数据库

    Args:
        excel_file_path: Excel 文件路径
        batch_size: 批量处理大小
    """
    logger.info(f"=" * 80)
    logger.info(f"开始导入机场数据")
    logger.info(f"文件路径: {excel_file_path}")
    logger.info(f"=" * 80)

    try:
        # 读取 Excel 文件
        airport_data_list = read_excel_file(excel_file_path)
        logger.info(f"成功读取 Excel 文件，共 {len(airport_data_list)} 条机场数据")

        # 首先初始化数据库（确保表结构正确）
        logger.info("正在初始化数据库...")
        from src.situation.database import init_db
        await init_db()
        logger.info("数据库初始化完成")

        # 创建数据库会话
        async for db in get_db_session():
            success_count = 0
            error_count = 0
            skipped_count = 0
            error_details = []

            for i, airport_data in enumerate(airport_data_list, 1):
                try:
                    logger.info(f"\n{'=' * 60}")
                    logger.info(f"处理第 {i}/{len(airport_data_list)} 条数据")

                    # 转换为态势对象格式
                    situation_data = convert_to_situation_object(airport_data)

                    logger.info(f"机场名称: {situation_data['name']}")
                    logger.info(f"机场编码: {situation_data.get('code', '无')}")

                    # 创建对象
                    obj_create = SituationObjectCreate(**situation_data)

                    # 创建态势对象 (SituationService.create_object 已经内置了编码重复检查)
                    created_object = await SituationService.create_object(
                        db, obj_create, user="airport_import_script"
                    )
                    logger.info(f"✓ 成功创建态势对象: {created_object.name} (ID: {created_object.id})")
                    success_count += 1

                except ValueError as e:
                    error_msg = str(e)
                    if "已存在" in error_msg or "重复" in error_msg:
                        logger.warning(f"✗ 数据已存在，跳过: {error_msg}")
                        skipped_count += 1
                        error_details.append({
                            'index': i,
                            'name': airport_data.get('名称', '未知'),
                            'reason': '数据已存在',
                            'error': error_msg
                        })
                    else:
                        logger.error(f"✗ 数据验证错误: {error_msg}")
                        error_count += 1
                        error_details.append({
                            'index': i,
                            'name': airport_data.get('名称', '未知'),
                            'reason': '数据验证失败',
                            'error': error_msg
                        })
                except Exception as e:
                    logger.error(f"✗ 处理第 {i} 条数据失败: {e}")
                    error_count += 1
                    error_details.append({
                        'index': i,
                        'name': airport_data.get('名称', '未知'),
                        'reason': '处理失败',
                        'error': str(e)
                    })
                    continue

            # 输出导入摘要
            logger.info(f"\n{'=' * 80}")
            logger.info("导入完成！")
            logger.info(f"{'=' * 80}")
            logger.info(f"总计: {len(airport_data_list)} 条")
            logger.info(f"成功: {success_count} 条")
            logger.info(f"跳过: {skipped_count} 条（已存在）")
            logger.info(f"失败: {error_count} 条")
            logger.info(f"{'=' * 80}")

            # 如果有错误，输出错误详情
            if error_details:
                logger.info("\n错误详情：")
                for detail in error_details:
                    logger.info(f"  第 {detail['index']} 条 - {detail['name']}: {detail['reason']}")
                    logger.debug(f"    错误信息: {detail['error']}")

            return {
                'total': len(airport_data_list),
                'success': success_count,
                'skipped': skipped_count,
                'error': error_count,
                'error_details': error_details
            }

    except Exception as e:
        logger.error(f"导入过程中发生严重错误: {e}")
        import traceback
        logger.error(traceback.format_exc())
        raise


async def main():
    """主函数"""
    # 默认文件路径
    default_file = "test/data-situation/airport.xlsx"

    # 检查命令行参数
    if len(sys.argv) > 1:
        excel_file = sys.argv[1]
    else:
        excel_file = default_file

    # 检查文件是否存在
    if not os.path.exists(excel_file):
        logger.error(f"文件不存在: {excel_file}")
        logger.info("用法: python scripts/import_airport_data.py [excel_file_path]")
        sys.exit(1)

    try:
        # 确保日志目录存在
        os.makedirs("logs", exist_ok=True)

        # 导入数据
        await import_airport_data(excel_file)

        logger.info("机场数据导入成功完成！")

    except Exception as e:
        logger.error(f"导入失败: {e}")
        sys.exit(1)


def run_main():
    """运行主函数的同步包装器"""
    asyncio.run(main())


if __name__ == "__main__":
    run_main()
